#include	"compiler.h"
#include	"strres.h"
#include	"dosio.h"
#include	"sysmng.h"
#include	"cpucore.h"
#include	"pccore.h"
#include	"sxsi.h"


const char sig_vhd[8] = "VHD1.00";
const char sig_nhd[15] = "T98HDDIMAGE.R0";

const SASIHDD sasihdd[7] = {
				{33, 4, 153},			// 5MB
				{33, 4, 310},			// 10MB
				{33, 6, 310},			// 15MB
				{33, 8, 310},			// 20MB
				{33, 4, 615},			// 20MB (not used!)
				{33, 6, 615},			// 30MB
				{33, 8, 615}};			// 40MB

#if 0
static const _SXSIDEV defide = {615*33*8, 615, 256, 33, 8,
								SXSITYPE_IDE | SXSITYPE_HDD, 256, 0, {0x00}};
static const _SXSIDEV defscsi = {40*16*32*8, 40*16, 256, 32, 8,
								SXSITYPE_SCSI | SXSITYPE_HDD, 220, 0, {0x00}};
#endif


	_SXSIDEV	sxsi_dev[SASIHDD_MAX + SCSIHDD_MAX];


// SASI規格HDDかチェック
static void sasihddcheck(SXSIDEV sxsi) {

const SASIHDD	*sasi;
	UINT		i;

	sasi = sasihdd;
	for (i=0; i<sizeof(sasihdd)/sizeof(SASIHDD); i++, sasi++) {
		if ((sxsi->size == 256) &&
			(sxsi->sectors == sasi->sectors) &&
			(sxsi->surfaces == sasi->surfaces) &&
			(sxsi->cylinders == sasi->cylinders)) {
			sxsi->type = (UINT16)(SXSITYPE_SASI + (i << 8) + SXSITYPE_HDD);
			break;
		}
	}
}


// ----

void sxsi_initialize(void) {

	UINT	i;

	ZeroMemory(sxsi_dev, sizeof(sxsi_dev));
	for (i=0; i<(sizeof(sxsi_dev)/sizeof(_SXSIDEV)); i++) {
		sxsi_dev[i].fh = (long)FILEH_INVALID;
	}
}

SXSIDEV sxsi_getptr(REG8 drv) {

	UINT	num;

	num = drv & 0x0f;
	if (!(drv & 0x20)) {					// SASI or IDE
		if (num < SASIHDD_MAX) {
			return(sxsi_dev + num);
		}
	}
#if defined(SUPPORT_SCSI)
	else {
		if (num < SCSIHDD_MAX) {			// SCSI
			return(sxsi_dev + SASIHDD_MAX + num);
		}
	}
#endif
	return(NULL);
}

const char *sxsi_getname(REG8 drv) {

	SXSIDEV	sxsi;

	sxsi = sxsi_getptr(drv);
	if (sxsi) {
		return(sxsi->fname);
	}
	return(NULL);
}

BOOL sxsi_hddopen(REG8 drv, const char *file) {

	SXSIDEV	sxsi;
	FILEH	fh;
const char	*ext;
	UINT16	type;
	long	totals;
	UINT32	headersize;
	UINT32	surfaces;
	UINT32	cylinders;
	UINT32	sectors;
	UINT32	size;

	if ((file == NULL) || (file[0] == '\0')) {
		goto sxsiope_err1;
	}
	sxsi = sxsi_getptr(drv);
	if (sxsi == NULL) {
		goto sxsiope_err1;
	}
	fh = file_open(file);
	if (fh == FILEH_INVALID) {
		goto sxsiope_err1;
	}
	ext = file_getext((char *)file);
	type = SXSITYPE_HDD;
	if ((!file_cmpname(ext, str_thd)) && (!(drv & 0x20))) {
		THDHDR thd;						// T98 HDD (IDE)
		if (file_read(fh, &thd, sizeof(thd)) != sizeof(thd)) {
			goto sxsiope_err2;
		}
		headersize = 256;
		surfaces = 8;
		cylinders = LOADINTELWORD(thd.cylinders);
		sectors = 33;
		size = 256;
		totals = cylinders * sectors * surfaces;
	}
	else if ((!file_cmpname(ext, str_nhd)) && (!(drv & 0x20))) {
		NHDHDR nhd;						// T98Next HDD (IDE)
		if ((file_read(fh, &nhd, sizeof(nhd)) != sizeof(nhd)) ||
			(memcmp(nhd.sig, sig_nhd, 15))) {
			goto sxsiope_err2;
		}
		headersize = LOADINTELDWORD(nhd.headersize);
		surfaces = LOADINTELWORD(nhd.surfaces);
		cylinders = LOADINTELDWORD(nhd.cylinders);
		sectors = LOADINTELWORD(nhd.sectors);
		size = LOADINTELWORD(nhd.sectorsize);
		totals = cylinders * sectors * surfaces;
	}
	else if ((!file_cmpname(ext, str_hdi)) && (!(drv & 0x20))) {
		HDIHDR hdi;						// ANEX86 HDD (SASI) thanx Mamiya
		if (file_read(fh, &hdi, sizeof(hdi)) != sizeof(hdi)) {
			goto sxsiope_err2;
		}
		headersize = LOADINTELDWORD(hdi.headersize);
		surfaces = LOADINTELDWORD(hdi.surfaces);
		cylinders = LOADINTELDWORD(hdi.cylinders);
		sectors = LOADINTELDWORD(hdi.sectors);
		size = LOADINTELDWORD(hdi.sectorsize);
		totals = cylinders * sectors * surfaces;
	}
	else if ((!file_cmpname(ext, str_hdd)) && (drv & 0x20)) {
		VHDHDR vhd;						// Virtual98 HDD (SCSI)
		if ((file_read(fh, &vhd, sizeof(vhd)) != sizeof(vhd)) ||
			(memcmp(vhd.sig, sig_vhd, 5))) {
			goto sxsiope_err2;
		}
		headersize = sizeof(vhd);
		surfaces = vhd.surfaces;
		cylinders = LOADINTELWORD(vhd.cylinders);
		sectors = vhd.sectors;
		size = LOADINTELWORD(vhd.sectorsize);
		totals = (SINT32)LOADINTELDWORD(vhd.totals);
	}
	else {
		goto sxsiope_err2;
	}

	// フォーマット確認〜
	if ((surfaces == 0) || (surfaces >= 256) ||
		(cylinders == 0) || (cylinders >= 65536) ||
		(sectors == 0) || (sectors >= 256) ||
		(size == 0) || ((size & (size - 1)) != 0)) {
		goto sxsiope_err2;
	}
	if (!(drv & 0x20)) {
		type |= SXSITYPE_IDE;
	}
	else {
		type |= SXSITYPE_SCSI;
		if (!(size & 0x700)) {			// not 256,512,1024
			goto sxsiope_err2;
		}
	}
	sxsi->totals = totals;
	sxsi->cylinders = (UINT16)cylinders;
	sxsi->size = (UINT16)size;
	sxsi->sectors = (UINT8)sectors;
	sxsi->surfaces = (UINT8)surfaces;
	sxsi->type = type;
	sxsi->headersize = headersize;
	sxsi->fh = (long)fh;
	file_cpyname(sxsi->fname, file, sizeof(sxsi->fname));
	if (type == (SXSITYPE_IDE | SXSITYPE_HDD)) {
		sasihddcheck(sxsi);
	}
	return(SUCCESS);

sxsiope_err2:
	file_close(fh);

sxsiope_err1:
	return(FAILURE);
}

void sxsi_open(void) {

	int		i;
	REG8	drv;

	sxsi_trash();
	drv = 0;
	for (i=0; i<2; i++) {
		if (sxsi_hddopen(drv, np2cfg.sasihdd[i]) == SUCCESS) {
			drv++;
		}
	}
#if defined(SUPPORT_SCSI)
	drv = 0x20;
	for (i=0; i<4; i++) {
		if (sxsi_hddopen(drv, np2cfg.scsihdd[i]) == SUCCESS) {
			drv++;
		}
	}
#endif
}

void sxsi_flash(void) {

	SXSIDEV	sxsi;
	SXSIDEV	sxsiterm;

	sxsi = sxsi_dev;
	sxsiterm = sxsi + (sizeof(sxsi_dev)/sizeof(_SXSIDEV));
	while(sxsi < sxsiterm) {
		if ((FILEH)sxsi->fh != FILEH_INVALID) {
			file_close((FILEH)sxsi->fh);
			sxsi->fh = (long)FILEH_INVALID;
		}
		sxsi++;
	}
}

void sxsi_trash(void) {

	SXSIDEV	sxsi;
	SXSIDEV	sxsiterm;

	sxsi = sxsi_dev;
	sxsiterm = sxsi + (sizeof(sxsi_dev)/sizeof(_SXSIDEV));
	while(sxsi < sxsiterm) {
		if ((FILEH)sxsi->fh != FILEH_INVALID) {
			file_close((FILEH)sxsi->fh);
		}
		ZeroMemory(sxsi, sizeof(_SXSIDEV));
		sxsi->fh = (long)FILEH_INVALID;
		sxsi++;
	}
}

static SXSIDEV getdrive(REG8 drv) {

	SXSIDEV	ret;

	ret = sxsi_getptr(drv);
	if ((ret == NULL) || (ret->fname[0] == '\0')) {
		return(NULL);
	}
	if ((FILEH)ret->fh == FILEH_INVALID) {
		ret->fh = (long)file_open(ret->fname);
		if ((FILEH)ret->fh == FILEH_INVALID) {
			ret->fname[0] = '\0';
			return(NULL);
		}
	}
	sysmng_hddaccess(drv);
	return(ret);
}

BOOL sxsi_issasi(void) {

	REG8	drv;
	SXSIDEV	sxsi;
	BOOL	ret;
	UINT	sxsiif;

	ret = FALSE;
	for (drv=0x00; drv<0x04; drv++) {
		sxsi = sxsi_getptr(drv);
		if (sxsi) {
			sxsiif = sxsi->type & SXSITYPE_IFMASK;
			if (sxsiif == SXSITYPE_SASI) {
				ret = TRUE;
			}
			else if (sxsiif == SXSITYPE_IDE) {
				ret = FALSE;
				break;
			}
		}
	}
	return(ret);
}

BOOL sxsi_isscsi(void) {

	REG8	drv;
	SXSIDEV	sxsi;

	for (drv=0x20; drv<0x28; drv++) {
		sxsi = sxsi_getptr(drv);
		if ((sxsi) && (sxsi->type)) {
			return(TRUE);
		}
	}
	return(FALSE);
}

BOOL sxsi_iside(void) {

	REG8	drv;
	SXSIDEV	sxsi;

	for (drv=0x00; drv<0x04; drv++) {
		sxsi = sxsi_getptr(drv);
		if ((sxsi) && (sxsi->type)) {
			return(TRUE);
		}
	}
	return(FALSE);
}

REG8 sxsi_read(REG8 drv, long pos, BYTE *buf, UINT size) {

const _SXSIDEV	*sxsi;
	long		r;
	UINT		rsize;

	sxsi = getdrive(drv);
	if (sxsi == NULL) {
		return(0x60);
	}
	if ((pos < 0) || (pos >= sxsi->totals)) {
		return(0x40);
	}
	pos = pos * sxsi->size + sxsi->headersize;
	r = file_seek((FILEH)sxsi->fh, pos, FSEEK_SET);
	if (pos != r) {
		return(0xd0);
	}
	while(size) {
		rsize = min(size, sxsi->size);
		CPU_REMCLOCK -= rsize;
		if (file_read((FILEH)sxsi->fh, buf, rsize) != rsize) {
			return(0xd0);
		}
		buf += rsize;
		size -= rsize;
	}
	return(0x00);
}

REG8 sxsi_write(REG8 drv, long pos, const BYTE *buf, UINT size) {

const _SXSIDEV	*sxsi;
	long		r;
	UINT		wsize;

	sxsi = getdrive(drv);
	if (sxsi == NULL) {
		return(0x60);
	}
	if ((pos < 0) || (pos >= sxsi->totals)) {
		return(0x40);
	}
	pos = pos * sxsi->size + sxsi->headersize;
	r = file_seek((FILEH)sxsi->fh, pos, FSEEK_SET);
	if (pos != r) {
		return(0xd0);
	}
	while(size) {
		wsize = min(size, sxsi->size);
		CPU_REMCLOCK -= wsize;
		if (file_write((FILEH)sxsi->fh, buf, wsize) != wsize) {
			return(0x70);
		}
		buf += wsize;
		size -= wsize;
	}
	return(0x00);
}

REG8 sxsi_format(REG8 drv, long pos) {

const _SXSIDEV	*sxsi;
	long		r;
	UINT16		i;
	BYTE		work[256];
	UINT		size;
	UINT		wsize;

	sxsi = getdrive(drv);
	if (sxsi == NULL) {
		return(0x60);
	}
	if ((pos < 0) || (pos >= sxsi->totals)) {
		return(0x40);
	}
	pos = pos * sxsi->size + sxsi->headersize;
	r = file_seek((FILEH)sxsi->fh, pos, FSEEK_SET);
	if (pos != r) {
		return(0xd0);
	}
	FillMemory(work, sizeof(work), 0xe5);
	for (i=0; i<sxsi->sectors; i++) {
		size = sxsi->size;
		while(size) {
			wsize = min(size, sizeof(work));
			size -= wsize;
			CPU_REMCLOCK -= wsize;
			if (file_write((FILEH)sxsi->fh, work, wsize) != wsize) {
				return(0x70);
			}
		}
	}
	return(0x00);
}

